import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashSet;
import java.util.Set;

import org.junit.Before;
import org.junit.Test;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.ImportDeclaration;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.BodyDeclaration;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.comments.LineComment;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.NameExpr;
import com.github.javaparser.ast.expr.SimpleName;

/**
 * 
 * 适用于该类错误 Use a logger to log this exception.
 *
 */
public class CodeClean {

	Set<Path> ps;

	Class<?> loggerClazz;

	private String loggerStmtRegex;

	@Before
	public void prepare() throws Exception {

		loggerClazz = Class.forName("org.apache.log4j.Logger");

		loggerStmtRegex = ".*Logger\\.getLogger\\(.*\\).*;.*";

		// 添加源码扫描路径
		ps = scan(//
				"E:/java/j2ee/GZ-projects/yth/zsale/twbpmForYTH",

				"E:/java/j2ee/GZ-projects/yth/zsale/zsale-web/src/main/java"
		//
		);
	}

	@Test
	public void doClean() throws Exception {

		int count = 1;

		for (Path path : ps) {
			String oldCode = new String(Files.readAllBytes(path));
			File oldFile = path.toFile();
			String oldPath = oldFile.toString();
			String className = oldFile.getName().replaceAll("(\\S+)\\.java", "$1");
			if (oldCode.matches("([\\s|\\S]*)([\\r|\\n]\\s*)(\\S*)\\.printStackTrace\\(\\s*\\);\\s*([\\r|\\n])([\\s|\\S]*)")) {
				oldFile.renameTo(new File(oldPath + ".bak"));
				CompilationUnit cu = JavaParser.parse(oldCode);
				ClassOrInterfaceDeclaration testClass = cu.getClassByName(className).get();
				importLoggerPackageIfNecessary(cu);
				String loggerFieldName = addLoggerFieldIfNecessary(className, testClass);
				String newCode = replace(cu.toString(), loggerFieldName);
				System.out.println(String.format("%d. %s ==> %s", count++, className, oldPath));
				saveFile(oldPath, newCode);
			}
		}
		System.out.println("\n整理完成。。。");
	}

	@Test
	public void doRecovery() throws IOException {
		int count = 1;
		for (Path path : ps) {
			File file = path.toFile();
			String filePath = file.toString();
			File bak = new File(filePath + ".bak");
			if (bak.exists()) {
				file.delete();
				bak.renameTo(new File(filePath));
				System.out.println(String.format("%d. %s", count++, filePath));
			}
		}
		System.out.println("\n恢复完成。。。");
	}

	@Test
	public void doDeleteBakupFile() throws IOException {
		int count = 1;
		for (Path path : ps) {
			File file = path.toFile();
			String filePath = file.toString();
			File bak = new File(filePath + ".bak");
			if (bak.exists()) {
				bak.delete();
				System.out.println(String.format("%d. %s", count++, filePath));
			}
		}
		System.out.println("\n删除完成。。。");
	}

	private void saveFile(String oldPath, String code) throws IOException {
		Files.write(Paths.get(oldPath), code.getBytes());
	}

	private Set<Path> scan(String... dirs) throws IOException {
		final Set<Path> ps = new HashSet<Path>();
		for (String dir : dirs) {
			Files.walkFileTree(Paths.get(dir), new SimpleFileVisitor<Path>() {
				@Override
				public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
					if (file.getFileName().toString().endsWith(".java")) {
						ps.add(file);
					}
					return FileVisitResult.CONTINUE;
				}
			});
		}
		return ps;
	}

	private String replace(String txt, String loggerFieldName) {
		return txt
				// 替换 e.printStackTrace();
				.replaceAll("([\\r|\\n]\\s*)(\\S*)\\.printStackTrace\\(\\s*\\);\\s*([\\r|\\n])", "$1" + loggerFieldName + ".info($2);$3")
				// 清除TODO
				.replaceAll("([\\r|\\n])(.*// TODO.*)([\\r|\\n])", "");
	}

	private void importLoggerPackageIfNecessary(CompilationUnit cu) {
		NodeList<ImportDeclaration> imports = cu.getImports();
		for (ImportDeclaration imp : imports) {
			if (imp.getNameAsString().equals(loggerClazz.getName())) {
				return;
			}
		}
		cu.addImport(loggerClazz);
	}

	private String addLoggerFieldIfNecessary(String className, ClassOrInterfaceDeclaration testClass) {
		NodeList<BodyDeclaration<?>> members = testClass.getMembers();
		for (BodyDeclaration<?> bd : members) {
			if (bd instanceof FieldDeclaration) {
				FieldDeclaration f = (FieldDeclaration) bd;
				// 此处
				if (f.toString().matches(loggerStmtRegex)) {
					String loggerFieldName = f.getVariable(0).getNameAsString();
					return loggerFieldName;
				}
			}
		}
		VariableDeclarator var = new VariableDeclarator();
		NameExpr clazz = new NameExpr("Logger");
		MethodCallExpr call = new MethodCallExpr(clazz, "getLogger");
		call.addArgument(className + ".class");
		var.setInitializer(call);
		var.setName(new SimpleName("logger"));
		var.setType(loggerClazz);
		FieldDeclaration field = new FieldDeclaration();
		field.setVariables(NodeList.nodeList(var));
		field.setStatic(true).setPrivate(true).setFinal(true);
		field.setComment(new LineComment(" 工具生成"));
		members.addFirst(field);
		return "logger";
	}

}
